﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WorldSimulator;
using WorldSimulator.PhysicEngine;
using WorldSimulator.PhysicsEngine;
using WorldSimulator.RenderingEngine;
using WorldSimulatorWPF.ViewModel;

namespace WorldSimulatorWPF
{
	/// <summary>
	/// Interaktionslogik für RenderWindow.xaml
	/// </summary>
	public partial class RenderWindow : UserControl, IRenderingEngine
	{
		public RenderWindow()
		{
			InitializeComponent();
		}

		private Dictionary<IPhysicalObject, ModelVisual3D> _objects = new Dictionary<IPhysicalObject, ModelVisual3D>();
		
		/// <summary>
		/// Rendered objects
		/// </summary>
		List<SphereViewModel> spheres = new List<SphereViewModel>();

		/// <summary>
		/// Registers the objects for rendering
		/// </summary>
		/// <param name="objects">Objects to register</param>
		public void RegisterObjects(IEnumerable<IPhysicalObject> objects)
		{
			foreach (var obj in objects)
				this.RegisterObject(obj);
		}

		/// <summary>
		/// Unregisters the objects for rendering
		/// </summary>
		/// <param name="objects">Objects to unregister</param>
		public void UnregisterObjects(IEnumerable<IPhysicalObject> objects)
		{
			foreach (var obj in objects)
				this.UnregisterObject(obj);
		}

		/// <summary>
		/// Registers an object for rendering
		/// </summary>
		/// <param name="obj">Physical object</param>
		public void RegisterObject(IPhysicalObject obj)
		{
			this.Dispatcher.Invoke(() =>
				{
					var model = new ModelVisual3D();
					var translateTransform = new TranslateTransform3D(obj.Location.X, obj.Location.Y, obj.Location.Z);
					var centerTransform = new TranslateTransform3D(obj.Radius / 2, obj.Radius / 2, obj.Radius / 2);
					var scaleTransform = new ScaleTransform3D();
					scaleTransform.CenterX = 0.5;
					scaleTransform.CenterY = 0.5;
					scaleTransform.CenterZ = 0.5;
					var transformGroup = new Transform3DGroup();
					transformGroup.Children.Add(scaleTransform);
					transformGroup.Children.Add(centerTransform);
					transformGroup.Children.Add(translateTransform);
					model.Transform = transformGroup;

					var geometry = (Geometry3D)this.FindResource("SphereMesh");
					var material = (Material)this.FindResource("SphereMaterial");
					model.Content = new GeometryModel3D(geometry, material);

					World.Children.Add(model);

					SphereViewModel vm = new SphereViewModel(obj);
					Binding bX = new Binding("X");
					bX.Source = vm;
					BindingOperations.SetBinding(translateTransform, TranslateTransform3D.OffsetXProperty, bX);
					Binding bY = new Binding("Y");
					bY.Source = vm;
					BindingOperations.SetBinding(translateTransform, TranslateTransform3D.OffsetYProperty, bY);
					Binding bZ = new Binding("Z");
					bZ.Source = vm;
					BindingOperations.SetBinding(translateTransform, TranslateTransform3D.OffsetZProperty, bZ);
					Binding bScale = new Binding("Radius");
					bScale.Source = vm;
					BindingOperations.SetBinding(scaleTransform, ScaleTransform3D.ScaleXProperty, bScale);
					BindingOperations.SetBinding(scaleTransform, ScaleTransform3D.ScaleYProperty, bScale);
					BindingOperations.SetBinding(scaleTransform, ScaleTransform3D.ScaleZProperty, bScale);
					this._objects.Add(obj, model);

					this.spheres.Add(vm);
				});
		}

		/// <summary>
		/// Unregisters an object for rendering
		/// </summary>
		/// <param name="obj">Physical object</param>
		public void UnregisterObject(IPhysicalObject obj)
		{

		}

		/// <summary>
		/// Renders a frame
		/// </summary>
		public void Render()
		{
			this.spheres.ForEach(x => x.RaisePropertyChanged());
		}


		private bool _3DLookaroundMode = false;
		private Point _lastMousePosition = new Point();

		private void Viewport3D_MouseDown(object sender, MouseButtonEventArgs e)
		{
			if (e.LeftButton == MouseButtonState.Pressed)
			{
				this._3DLookaroundMode = true;
				this._lastMousePosition = e.GetPosition(this);
			}
		}

		private void Viewport3D_MouseLeave(object sender, MouseEventArgs e)
		{
			this._3DLookaroundMode = false;
		}

		private void Viewport3D_MouseMove(object sender, MouseEventArgs e)
		{
			if (e.LeftButton != MouseButtonState.Pressed)
				return;

			if (this._3DLookaroundMode)
			{
				var currentPos = e.GetPosition(this);
				var distanceX = currentPos.X - this._lastMousePosition.X;
				var distanceY = currentPos.Y - this._lastMousePosition.Y;

				this.CameraYRotation.Angle += distanceX;
				this.CameraXRotation.Angle += distanceY;
				
				this._lastMousePosition = currentPos;
			}
		}
	}
}
